#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <netdb.h> 
#include <netinet/in.h>
#include <regex.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/select.h>
#include <sys/shm.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <termios.h>
#include <unistd.h>



#define VGDB_DEBUG
#ifdef VGDB_DEBUG
#define DEBUG_MESSAGE(...)                                             \
do{                                                                    \
    char vgdb_debug_buff[1024];                                        \
    char vgdb_debug_cmd[128];                                          \
    sprintf(vgdb_debug_buff,__VA_ARGS__);                              \
    sprintf(vgdb_debug_cmd,"echo \"%s\" >>debug.log",vgdb_debug_buff); \
    system(vgdb_debug_cmd);                                            \
}while(0)
#else
#define DEBUG_MESSAGE(...)
#endif



#define VGDB_FNLEN      128





#define VGDB_VMT_INSERT 0
#define VGDB_VMT_DETACH 1
#define VGDB_VMT_OPENFN 2

#define VGDB_VMSG_ALEN  128
typedef struct {
    int     type;
    int     bid;
    char    args[VGDB_VMSG_ALEN];
}vmsg_t;





#define VGDB_GCT_FILE      0
#define VGDB_GCT_RUN       1
#define VGDB_GCT_STEP      2
#define VGDB_GCT_BREAK     3
#define VGDB_GCT_CONTINUE  4
#define VGDB_GCT_QUIT      5

typedef struct {
    int     type;
    int     stype;
    int     argc;
    char    argv[];
}gcmd_t;






#define VGDB_GS_QUIT   0
#define VGDB_GS_STOP   1
#define VGDB_GS_RUN    2

typedef struct {
    pid_t   pid;
    int     state;
    int     rwfd;
    char    symfn[VGDB_FNLEN];
}gdb_t;










#define VGDB_VS_DETACHED    0   /* 尚未链接*/
#define VGDB_VS_START       1   /* 链接但是没有开始debug*/
#define VGDB_VS_RUN         2   /* debug中*/

typedef struct {
    pid_t   pid;
    int     state;
    int     datsock;
}vim_t;







#define VGDB_BLEN       128
#define VGDB_BNR        64

#define VGDB_GCTLLEN    16
#define VGDB_GCTLNR     16

#define VGDB_VMTLLEN    16
#define VGDB_VMTLNR     16
typedef void (*gcmd_func)(gcmd_t*);
typedef void (*vmsg_func)(vmsg_t*);

typedef struct {

    pid_t       pid;
    int         state;

    char        buff[VGDB_BLEN*VGDB_BNR];
    int         blen;
    int         bnr;
    int         bcur;

    gcmd_func   gcmdtbl[VGDB_GCTLLEN*VGDB_GCTLNR];
    int         gctllen;
    int         gctlnr;

    vmsg_func   vmsgtbl[VGDB_VMTLLEN*VGDB_VMTLNR];
    int         vmtllen;
    int         vmtlnr;

    gdb_t       gdb;
    vim_t       vim;

}vgdb_t;

vgdb_t  g_server;

#define instance()  (&g_server)







static int get_vimmsg()
{
    // 始终使用缓冲区第0行接收
    // 因为vim每个msg都是一行
    // 并且来一个就必须处理一个

    // 急需修改, 现在只能勉强运行
    //         -1 : 读取出错
    //          0 : socket已经断开
    // 1 ~ size-1 : 正常读取
    //       size : 读满
    int         data_sock;
    int         recv_len;
    char       *bptr;
    int         blen;
    int         bnr;

    data_sock = instance()->vim.datsock;
    bptr      = instance()->buff;
    blen      = instance()->blen;
    bnr       = instance()->bnr;

    while((recv_len = recv(data_sock, (void*)bptr, 1, 0))>=0){
        // -1 错误
        //  0 链接已经断开
        // >0 读出字节数
        if(recv_len == 0){
            return 0;
        }else if(recv_len < 0){
            return -1;
        }else{
            if((*bptr == '\n') || (*bptr == '\0')){
                *bptr = '\0';
                break;
            }
            bptr++;
        }
    }
    return 1;
}

static int fdgetline(int fd, char *buff, int blen)
{
    // 急需修改, 现在只能勉强运行
    //         -1 : 读取出错
    //          0 : fd已经关闭
    // 1 ~ blen-1 : 正常读取(因为最后一位是\0)
    //       blen : 溢出错

    int len = 0;

    while(1){
        switch(read(fd, buff+len, 1)){
            case 1:
                if((*(buff+len) == '\n') || (*(buff+len) == '\0')){
                    *(buff+len) = '\0';
                    return len;
                }else{
                    len++;
                }
                break;
            case 0:
                return len;
            default:
                return -1;
        }
    }
}


static int fdgetlines(int fd, char *buff, int blen, int bnr, const char *end)
{
    // 急需修改, 现在只能勉强运行
    //      -1 : 读取出错
    //       0 : fd已经关闭
    // 1 ~ bnr : 正常读取行数
    //   bnr+1 : 行溢出
    //   bnr+2 : 行内溢出

    int ind = 0;
    while(ind < bnr){
        if(fdgetline(fd, buff+ind*blen, blen)>=0){
            if(!strcmp(buff+ind*blen, end)){
                return ind;
            }else{
                ind++;
            }
        }else{
            return ind;
        }
    }
    return bnr+1;
}

static void get_gdboutp()
{
    instance()->bcur = fdgetlines(
            instance()->gdb.rwfd, 
            instance()->buff+instance()->blen,
            instance()->blen,
            instance()->bnr-1,
            "\x1a\x1aprompt") -3;
}

/***********************************************************************************************************
 * gdb cmd
 * 
 */

static void gcmd_quit(int argc, char* argv)
{
    char       *cmd  = "quit\n";

    write(instance()->gdb.rwfd, cmd, strlen(cmd));
    waitpid(instance()->gdb.pid, NULL, 0);
    DEBUG_MESSAGE("gdb quit sucessfully.");
    instance()->gdb.state = VGDB_GS_QUIT;
}

static void exec_gcmd(gcmd_t *gcmd){
    // 执行一个gdb的命令
    // 并且等待该命令导致的gdb的所有副作用全部生效再返回
    // 但是只是gdb的副作用
    // 对其他的副作用不能写在这里面
    ((instance()->gcmdtbl)[gcmd->type*instance()->gctllen+gcmd->stype])(gcmd);
}





static void exec_gdb()
{
    struct termios  stermios;

    if(tcgetattr(STDIN_FILENO,&stermios) >= 0) {
        stermios.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
        stermios.c_oflag &= ~(ONLCR);
        tcsetattr(STDIN_FILENO,TCSANOW,&stermios);
    }
    execlp("gdb","gdb","--annotate=3",(char *) NULL);
}


static void send_vmsg(vmsg_t *vmsg)
{
    (instance()->vmsgtbl[vmsg->type])(vmsg);
}

static int vim_init_response()
{
    // <0 : 错误
    //  0 : vim退出
    // >0 : 初始化消息的条数
    char       *bptr;
    int         ret;
    gcmd_t      gcmd;
    vmsg_t      vmsg;
    int         count = 0;

    DEBUG_MESSAGE("in vim_init_response()");
    bptr = instance()->buff;
    while(1){
        ret = get_vimmsg();
        switch(get_vimmsg()){
            case -1:
                // recv出错
                return -1;
            case 0:
                // 链接已经断开
                // 正常情况下这里不可能执行
                gcmd.type  = VGDB_GCT_QUIT;
                exec_gcmd(&gcmd);

                vmsg.type  = VGDB_VMT_INSERT;
                strcpy(vmsg.args, "gdb quit, detached...");
                send_vmsg(&vmsg);

                vmsg.type  = VGDB_VMT_DETACH;
                send_vmsg(&vmsg);
                // 这里暂时没有close（socket）
                // 因为有可能重新开始调试
                return 0;
            default:
                DEBUG_MESSAGE("vimmsg %2d |%s",count, instance()->buff);
                count++;
                if(!strcmp(bptr, "0:startupDone=0")){
                    DEBUG_MESSAGE("get vim init response successfully.");
                    instance()->vim.state  = VGDB_VS_START;
                    DEBUG_MESSAGE("out vim_init_response()");
                    return 1;
                }else if(!strcmp(bptr, "0:disconnect=0")){
                    close(instance()->vim.datsock);
                    DEBUG_MESSAGE("close server data socket");
                    instance()->vim.state = VGDB_VS_DETACHED;
                    //DETACH
                    return 0;
                }
        }
    }
}


static int get_evtid(const char *vimmsg)
{
    // 先实现的简单点

    if(!strcmp(vimmsg, "0:disconnect:0")){
        return 0;
    }else{
        return 1;
    }
}

static void gdb_init_response()
{
    int ret, i;
    DEBUG_MESSAGE("in gdb_init_response()");
    ret = fdgetlines( instance()->gdb.rwfd,
            instance()->buff, 
            instance()->blen, 
            instance()->bnr, 
            "\x1a\x1aprompt");
    for(i=0;i<ret;++i){
        DEBUG_MESSAGE("%s",instance()->buff+i*instance()->blen);
    }
    DEBUG_MESSAGE("out gdb_init_response()");
}
static int process_vimmsg()
{
    char*   vimmsg;
    int     id;

    vimmsg = instance()->buff;
    id     = get_evtid(vimmsg);
    if(id){
        // 事件
        //(instance()->evttbl[id])(vimmsg);
    }else{
        // 函数应答
    }
    return 1;
}

static void vgdb_run()
{
    vim_init_response();
    //gdb_init_response();

    while(1){
        // 被动模式, server不会自发向vim发送消息
        if(!process_vimmsg()){
            break;
        }
    }
}

static int connect_to_vim()
{    
    char   *hostname   =   "localhost";
    //char   *password   =   "changeme";
    int     port       =   3219;

    int     listen_socket;
    int     accept_socket;
    //int     opt;

    struct sockaddr_in  socket_addr;
    struct hostent     *host;
    socklen_t           socket_len;

    if ((listen_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        DEBUG_MESSAGE("socket() failed");
        return 1;
    }

    memset((void *)&socket_addr, 0, sizeof(socket_addr));
    socket_addr.sin_family = AF_INET;
    socket_addr.sin_port = htons(port);
    if ((host = gethostbyname(hostname)) == NULL) {
        close(listen_socket);
        DEBUG_MESSAGE("gethostbyname() failed");
        return 2;
    }
    memcpy((void *)&socket_addr.sin_addr, host->h_addr, (size_t)host->h_length);

    //opt = 1;
    //setsockopt(listen_socket,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

    if (bind(listen_socket, (struct sockaddr *)&socket_addr, sizeof(socket_addr)) == -1) {
        close(listen_socket);
        DEBUG_MESSAGE("bind() failed");
        return 3;
    }

    if (listen(listen_socket, 5) == -1 ) {
        close( listen_socket );
        DEBUG_MESSAGE("listen() failed");
        return 4;
    }

    socket_len = (socklen_t)sizeof(socket_addr);

    if ((accept_socket = accept(listen_socket, (struct sockaddr *)&socket_addr, &socket_len)) == -1) {
        close(listen_socket);
        DEBUG_MESSAGE("accept() failed");
        return 5;
    }
    close( listen_socket );
    instance()->vim.datsock = accept_socket;
    DEBUG_MESSAGE("connect to vim sucessfully");
    return 0;
}


const char *VgdbStartServer(const char *arg)
{
    pid_t   svr_pid;

    instance()->vim.pid = getpid();
    //strcpy(instance()->arg, arg);
    strcpy(instance()->gdb.symfn, arg);

    svr_pid = fork();
    switch(svr_pid){
        case -1:
            return "error: fork() failed";
        case 0:
            instance()->pid = getpid();
            vgdb_run();
            return "error: can not come here";
        default:
            return "ok";
    }
}
